package com.sneakxy.mybatis.commons.utils;
/**
 * 雪花算法ID实现
 * @author 潜行的虚影
 */
public class SnowflakeIdGeneratorImpl implements SnowflakeIdGenerator {
	
	private long epoch;
	
	private long workIdBits;
	
	private long maxWorkId;
	
	private long workId;
	
	private final long sequenceBits = 12L;
	
	private final long maxSequence = ~ (-1L << sequenceBits);
	
	private long sequence;
	
	private long sequenceLeftOffset;
	
	private long timeLeftOffset;
	
	private long lastTimeMillis;
	
	public SnowflakeIdGeneratorImpl(long workIdBits, long workId) {
		this(DEFAULT_EPOCH, workIdBits, workId);
	}
	
	public SnowflakeIdGeneratorImpl(long epoch, long workIdBits, long workId) {
		if(workIdBits < 5 || workIdBits > 10) {
			throw new IllegalArgumentException("workIdBits范围错误.");
		}
		this.workIdBits = workIdBits;
		this.maxWorkId = ~ (-1L << workIdBits);
		if(workId < 0 || workId > maxWorkId) {
			throw new IllegalArgumentException("workId范围错误.");
		}
		this.epoch = epoch;
		this.workId = workId;
		this.sequenceLeftOffset = workIdBits;
		this.timeLeftOffset = sequenceBits + workIdBits;
	}
	
	@Override
	public synchronized long generateId() {
		long time = currentTimeMillis();
		if(time < lastTimeMillis) {
			throw new RuntimeException("系统时间被回调.");
		}
		if(time == getLastTimeMillis()) {
			sequence = (sequence + 1) & getMaxSequence();
			if(sequence == 0) {
				time = nextTimeMillis();
			}
		} else {
			sequence = 0;
		}
		lastTimeMillis = time;
		return ((time - getEpoch()) << getTimeLeftOffset()) | (getSequence() << getSequenceLeftOffset()) | getWorkId();
	}
	
	protected long nextTimeMillis() {
		long result = 0;
		while((result = currentTimeMillis()) <= getLastTimeMillis()) {}
		return result;
	}
	
	public long currentTimeMillis() {
		return System.currentTimeMillis();
	}

	public long getWorkIdBits() {
		return workIdBits;
	}

	public long getMaxWorkId() {
		return maxWorkId;
	}

	public long getWorkId() {
		return workId;
	}

	public long getSequenceBits() {
		return sequenceBits;
	}

	public long getMaxSequence() {
		return maxSequence;
	}

	public long getSequence() {
		return sequence;
	}

	public long getSequenceLeftOffset() {
		return sequenceLeftOffset;
	}

	public long getTimeLeftOffset() {
		return timeLeftOffset;
	}

	public long getLastTimeMillis() {
		return lastTimeMillis;
	}

	public long getEpoch() {
		return epoch;
	}

}
